package bungaeting.net.client.android.task;

import java.util.AbstractQueue;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedQueue;

import android.os.AsyncTask;
import bungaeting.net.client.android.request.HttpRequestor;
import bungaeting.net.client.android.request.Request;
import bungaeting.net.client.android.request.RequestObserver;
import bungaeting.net.client.android.request.Requestor;
import bungaeting.net.client.android.request.Response;

public class BackgroundService extends AsyncTask<Request, BackgroundServiceProgress, BackgroundServiceResult> {
	
	private final static int MAX_ACITVE_REQUEST_COUNT_DEFAULT = 3;
	private final int maxAcitveRequestCount;
	private AbstractQueue<Request> queueRequest;
	private AbstractQueue<Response> queueResponse;
	private Map<String, RequestObserver> mapRequestObserver;
	private Requestor requestor;
	private Vector<Thread> activeRequest;
	private boolean enabled;
	
	public BackgroundService() {
		this(MAX_ACITVE_REQUEST_COUNT_DEFAULT);
	}
	public BackgroundService(int maxAcitveRequestCount) {
		this.maxAcitveRequestCount = maxAcitveRequestCount;
		queueRequest = new ConcurrentLinkedQueue<Request>();
		queueResponse = new ConcurrentLinkedQueue<Response>();
		mapRequestObserver = Collections.synchronizedMap(new HashMap<String, RequestObserver>());
		activeRequest = new Vector<Thread>();
		enabled = true;
		requestor = new HttpRequestor();
	}

	@Override
	protected BackgroundServiceResult doInBackground(
			Request... arg0) {
		while (enabled){
			while (canProcessRequest() && processReqeust());
			
			while (processResponse());
			try {
				Thread.sleep(getSleepTime());
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
	
	protected long getSleepTime(){
		return 1000;
	}
	
	protected boolean canProcessRequest(){
		return activeRequest.size() < this.maxAcitveRequestCount;
	}
	protected boolean processReqeust(){
		if (queueRequest.size() == 0)
			return false;
		
		if (!canProcessRequest())
			return false;
		
		final Request request = dequeueRequest();
		Thread requestThread  = new Thread(new Runnable(){
			@Override
			public void run() {
				try {
					Thread.sleep(5L);
				} catch (InterruptedException e) {
					System.err.println("processRequest sleep failed.");
					e.printStackTrace();
				}
				// TODO Auto-generated method stub
				enqueueResponse(requestor.doRequest(request));
				
				activeRequest.remove(this);
			}
		});
		requestThread.start();
		activeRequest.add(requestThread);
		
		if (queueRequest.size() > 0)
			return true;
		else
			return false;
	}
	protected boolean processResponse(){
		if (queueResponse.size() == 0)
			return false;
		
		Response response = dequeueResponse();
		RequestObserver requestObserver = mapRequestObserver.get(response.getServiceId());
		if (requestObserver.getProcessThreadType() == RequestObserver.THREAD_TYPE.UI_THREAD){
			publishProgress(
					new BackgroundServiceProgress(
							BackgroundServiceProgress.TYPE.RESPONSE_PROCESS, response));
		}
		else{
			processRequestObserverUpdate(response);
		}
		
		if (queueResponse.size() > 0)
			return true;
		else
			return false;
	}
	
	protected void processRequestObserverUpdate(Response response){
		RequestObserver requestObserver = mapRequestObserver.get(response.getServiceId());
		requestObserver.update(response.getRequestId(), response.getReturnCode(), response.getReturnMsg(), response.getParams());
	}
	
	@Override
	protected void onProgressUpdate(BackgroundServiceProgress... progresses) {
		for (BackgroundServiceProgress progress : progresses){
			processRequestObserverUpdate(progress.getResponse());
		}
		super.onProgressUpdate(progresses);
	}

	protected boolean enqueueRequest(Request request){
		queueRequest.offer(request);
		return true;
	}
	protected Request dequeueRequest(){
		return queueRequest.poll();
	}
	
	protected boolean enqueueResponse(Response response){
		queueResponse.offer(response);
		return true;
	}
	protected Response dequeueResponse(){
		return queueResponse.poll();
	}
	
	public void startWorkerThread(){
		this.execute();
	}
	public void stopWorkerThread(){
		enabled = false;
	}
	public void cancelWorkerThread(){
		this.cancel(true);
	}
	public boolean addObserver(String requestId, String serviceId, RequestObserver observer){
		if (observer != null){
			mapRequestObserver.put(serviceId, observer);
		}
		return enqueueRequest(Request.createRequest("addObserver", requestId, "serviceId", serviceId));
	}
	
	public boolean requestAsync(String serviceId, String requestId, Map<String, Object> params, RequestObserver observer){
		if (observer != null){
			mapRequestObserver.put(serviceId, observer);
		}
		return enqueueRequest(Request.createRequest(serviceId, requestId, params));
	}
	public boolean requestSync(String serviceId, String requestId, Map<String, Object> params, RequestObserver observer){
		Response response = requestor.doRequest(Request.createRequest(serviceId, requestId, params));
		RequestObserver requestObserver = mapRequestObserver.get(response.getServiceId());
		requestObserver.update(response.getRequestId(), response.getReturnCode(), response.getReturnMsg(), response.getParams());
		return true;
	}

	static public String getServiceRequestId(String serviceId, String requestId){
		return serviceId + "." + requestId;
	}
}
